package my.ssm;


import my.util.StringUtil;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * @author zhupanlin
 * @version 1.0
 * @description: TODO
 * @date 2023/10/18 18:59
 */
public class DispatcherServlet extends ViewBaseServlet {

    private BeanFactory beanFactory;

    @Override
    public void init() throws ServletException {
        super.init();
        beanFactory = new ClassPathXmlApplicationContext();
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置字符集编码
        req.setCharacterEncoding("UTF-8");
        // 1. 通过servletPath 获取 路径名
        // 2. 加载配置文件，字符串与类对应的map
        // 3. 通过路径名调用对应的Controller

        // 假设url = http://localhost:8080/pro/hello.do
        // 那么 servletPath = /hello.do -> hello
        String servletPath = req.getServletPath();
        servletPath = servletPath.substring(1);
        int index = servletPath.indexOf(".do");
        servletPath = servletPath.substring(0, index);
        // 通过hello 得到 HelloController
        Object controllerBeanObj = beanFactory.getBean(servletPath);

        // 得到Controller中要执行的操作
        String operate = req.getParameter("operate");
        // 如果operate为空，则跳转到首页
        if (StringUtil.isEmpty(operate)){
            operate = "index";
        }

        try {
            Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
            for (Method method : methods) {
                // 在Controller中寻找匹配的方法名
                if (operate.equals(method.getName())){
                    // 1. 统一获取请求参数
                    // 1.1 获取当前方法的参数，返回参数数组
                    Parameter[] parameters = method.getParameters();
                    int len = parameters.length;
                    // 1.2 parameterValues 用来承载参数的值
                    Object[] parameterValues = new Object[len];
                    for (int i = 0; i < len; ++i) {
                        // 根据参数名用来获取方法中那个具体的参数
                        Parameter parameter = parameters[i];
                        String parameterName = parameter.getName();
                        // 如果参数名是request，response，session就直接赋给它
                        // 否则都是请求中的参数
                        if ("request".equals(parameterName)){
                            parameterValues[i] = req;
                        }else if ("response".equals(parameterName)){
                            parameterValues[i] = resp;
                        }else if ("session".equals(parameterName)){
                            parameterValues[i] = req.getSession();
                        }else {
                            // 从请求中获取参数
                            String parameterValue = req.getParameter(parameterName);
                            String typeName = parameter.getType().getName();
                            Object parameterObj = parameterValue;
                            if (parameterObj != null){
                                // 判断类型
                                if ("java.lang.Integer".equals(typeName)){
                                    parameterObj = Integer.parseInt(parameterValue);
                                }
                            }
                            parameterValues[i] = parameterObj;
                        }
                    }
                    // 2. Controller组件中方法调用
                    method.setAccessible(true);
                    Object returnObj = method.invoke(controllerBeanObj, parameterValues);
                    String methodReturnStr = (String) returnObj;
                    // 3. 视图处理
                    if (methodReturnStr.startsWith("redirect:")){
                        // 相应重定向 以 redirect: 开头
                        String redirectStr = methodReturnStr.substring("redirect:".length());
                        resp.sendRedirect(redirectStr);
                    } else {
                        /*3.调用父类的模板处理方法
                        第一个参数：逻辑视图名称（模板名称）
                        thymeleaf会将这个 逻辑视图名称 对应到 物理视图名称 上去
                        物理视图名称：view-prefix + 逻辑视图名称 + view-suffix
                        所以真实的视图名称：/   index   .html
                        这样就将web文件夹下的index.html文件上组装上了（跳转到该/index.html页面上了）
                        或者说该模板处理方法的第一个参数的作用就是配合ViewBaseServlet类中的init方法链接到指定的html文件(模板)上
                        接下来，就是在该html文件上使用thymeleaf的标签，将数据库中的数据动态的整合进来，达到渲染的目的*/
                        super.processtemplate(methodReturnStr, req, resp);
                    }
                }

            }
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }
}
